﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using Lextm.SharpSnmpLib;
using Lextm.SharpSnmpLib.Security;
using Lextm.SharpSnmpLib.Messaging;
using Lextm.SharpSnmpLib.Mib;
using System.IO;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using TopologyWizzard;

namespace VPN_Emulator
{
    //! Klasa obsługująca zapytania SNMP
    class SNMPController
    {
        private MainWindow mainWindow;
        private string start_OID;

        //! okno główne Aplikacji
        public MainWindow MainWindow
        {
            set
            {
                mainWindow = value;
            }
        }

        //! Obsługa operacji GET dla zapytań wysyłanych przez użytkownika
        public List<Variable> Get(List<string> oidList, string destIp, int port, string community, int timeout)
        {
            List<Variable> result = new List<Variable>();
            IPAddress ip;
            bool parsed = IPAddress.TryParse(destIp, out ip);

            if (!parsed)
            {
                if (destIp == null)
                {
                    Console.WriteLine("invalid host or wrong IP address found: ");
                    mainWindow.addConsoleText("invalid host or wrong IP address found", Color.Red);
                    return result;
                }

                foreach (IPAddress address in Dns.GetHostAddresses(destIp))
                {
                    if (address.AddressFamily == AddressFamily.InterNetwork)
                    {
                        ip = address;
                        break;
                    }
                }

                if (ip == null)
                {
                    Console.WriteLine("invalid host or wrong IP address found: " + destIp);
                    mainWindow.addConsoleText("invalid host or wrong IP address found: " + destIp, Color.Red);
                    return result;
                }
            }

            try
            {
                List<Variable> vList = new List<Variable>();

                Variable test = new Variable(new ObjectIdentifier(oidList[0]));
                vList.Add(test);


                IPEndPoint receiver = new IPEndPoint(ip, port);

                foreach (
                    Variable variable in
                        Messenger.Get(VersionCode.V1, receiver, new OctetString(community), vList, timeout))
                {
                    result.Add(variable);
                   
                }

                return result;

            }

            catch (SnmpException ex)
            {
                mainWindow.addConsoleText("SNMP exception: " + ex, Color.Red);
                return result;
            }
        }

        //! Obsługa operacja GET TABLE dla tablic VRF
        public List<List<String>> GetVrfTable(List<string> oidList, string destIp, int port, string community, int timeout)
        {
            
            List<List<String>> result_table = new List<List<string>>();

            for (int i = 0; i < oidList.Count; i++)
            {
               start_OID=oidList[i].Substring(0, 20);

            }

            List<string> currentOidList = oidList;
            List<string> column_list = new List<string>();
            List<string> vrfNameList = new List<string>();
            bool flag = true;
            try
            {
                do
                {

                    List<Variable> result = GetNext(currentOidList, destIp, port, community, timeout);


                    try
                    {
                        if (currentOidList[0].Length > 23 && (result[0].Id.ToString().Substring(23, 2) != currentOidList[0].Substring(23, 2)))
                        {
                            result_table.Add(column_list);
                            column_list = new List<string>();
                            flag = false;
                        }

                        currentOidList.Clear();

                        currentOidList.Add(result[0].Id.ToString());
                        if (flag)
                            vrfNameList.Add(translateName(currentOidList[0]));

                        column_list.Add(result[0].Data.ToString());
                    }
                    catch (ArgumentOutOfRangeException ex)
                    {
                    }
                    catch (NullReferenceException exx)
                    {
                    }

                }

                while (currentOidList[0].Substring(0, 20) == start_OID);
            }
            catch (ArgumentOutOfRangeException ex)
            {
            }
            result_table.Insert(0, vrfNameList);
            return result_table;
        }

        //! Obsługa operacja GET TABLE dla tablic Route Target
        public List<List<String>> GetRouteTargetTable(List<string> oidList, string destIp, int port, string community, int timeout)
        {

            List<List<String>> result_table = new List<List<string>>();

            for (int i = 0; i < oidList.Count; i++)
            {
                start_OID = oidList[i].Substring(0, 20);

            }

            List<string> currentOidList = oidList;
            List<string> column_list = new List<string>();
            List<string> vrfNameList = new List<string>();
            List<string> typeList = new List<string>();
            bool flag = true;
            try
            {
                do
                {

                    List<Variable> result = GetNext(currentOidList, destIp, port, community, timeout);


                    try
                    {
                        if (currentOidList[0].Length > 23 && (result[0].Id.ToString().Substring(23, 2) != currentOidList[0].Substring(23, 2)))
                        {
                            result_table.Add(column_list);
                            column_list = new List<string>();
                            flag = false;
                        }

                        currentOidList.Clear();

                        currentOidList.Add(result[0].Id.ToString());
                        if (flag)
                        {
                            vrfNameList.Add(translateName(currentOidList[0]));
                            typeList.Add(translateType(currentOidList[0]));
                        }
                        column_list.Add(result[0].Data.ToString());
                    }
                    catch (ArgumentOutOfRangeException ex)
                    {
                    }
                    catch (NullReferenceException exx)
                    {
                    }

                }

                while (currentOidList[0].Substring(0, 20) == start_OID);
            }

            catch (ArgumentOutOfRangeException ex)
            {
            }
            result_table.Insert(0, vrfNameList);
            result_table.Insert(1, typeList);
            return result_table;
        }

        //! Obsługa operacja GET TABLE dla tablic Route Table VRF
        public List<List<String>> GetVpnVrfRouteTable(List<string> oidList, string destIp, int port, string community, int timeout)
        {

            List<List<String>> result_table = new List<List<string>>();

            for (int i = 0; i < oidList.Count; i++)
            {
                start_OID = oidList[i].Substring(0, 20);

            }

            List<string> currentOidList = oidList;
            List<string> column_list = new List<string>();

            List<string> vpnVrfRouteDestList = new List<string>();
            List<string> vpnVrfRouteMask = new List<string>();
            List<string> vpnVrfRouteTos = new List<string>();
            List<string> vpnVrfRouteNextHop = new List<string>();

            bool flag = true;
            do
            {

                List<Variable> result = GetNext(currentOidList, destIp, port, community, timeout);


                try
                {
                    if (currentOidList[0].Length > 23 && (result[0].Id.ToString().Substring(23, 2) != currentOidList[0].Substring(23, 2)))
                    {
                        result_table.Add(column_list);
                        column_list = new List<string>();
                        flag = false;
                    }

                    currentOidList.Clear();
                    try
                    {
                        currentOidList.Add(result[0].Id.ToString());
                        if (flag)
                        {
                            int vrfName = getVrfNameLength(currentOidList[0]);
                            string[] words = currentOidList[0].Split('.');
                            vpnVrfRouteDestList.Add(words[14 + vrfName] + "." + words[15 + vrfName] + "." + words[16 + vrfName] + "." + words[17 + vrfName]);
                            vpnVrfRouteMask.Add(words[19 + vrfName] + "." + words[20 + vrfName] + "." + words[21 + vrfName] + "." + words[22 + vrfName]);
                            vpnVrfRouteTos.Add(words[23 + vrfName]);
                            vpnVrfRouteNextHop.Add(words[25 + vrfName] + "." + words[26 + vrfName] + "." + words[27 + vrfName] + "." + words[28 + vrfName]);



                        }
                        column_list.Add(result[0].Data.ToString());
                    }
                    catch (NullReferenceException ex)
                    {
                        break;
                    }
                }
                catch (ArgumentOutOfRangeException ex)
                {
                    break;
                }


            }

            while (currentOidList[0].Substring(0, 20) == start_OID);
            try
            {
                result_table.Insert(0, vpnVrfRouteDestList);
                result_table.Insert(2, vpnVrfRouteMask);
                result_table.RemoveAt(5);
                result_table.Insert(5, vpnVrfRouteNextHop);
            }
            catch(ArgumentOutOfRangeException ex)
            {
            }
           // result_table.Insert(5, vpnVrfRouteNextHop);
            return result_table;
        }

        //! Pobieranie długości nazwy tablic VRF
        private int getVrfNameLength(string oid)
        {
            string[] words = oid.Split('.');
            try
            {
                return Int32.Parse(words[12]);
            }
            catch (IndexOutOfRangeException ex)
            {
                mainWindow.addConsoleText("SNMP exception: " + "No VRF in this router", Color.Red);
                return 0;
            }
        }

        //! Pobranie typu zwracanej odpowiedzi SNMP
        private string translateType(string oid)
        {
            string[] words = oid.Split('.');
            string last_element = words[words.Length - 1];

            if (last_element == "1")
                return "import";
            else if (last_element == "2")
                return "export";
            else if (last_element == "3")
                return "both";

            return null;
        }

        //! Pobranie nazwy zwracanej odpowiedzi SNMP
        private string translateName(string oid)
        {
            string[] words = oid.Split('.');
            int nameLength;
            byte[] bytes;
            try
            {
                nameLength = Int32.Parse(words[12]);
                bytes = new byte[nameLength];

                for (int i = 0; i < nameLength; i++)
                {
                    bytes[i] = Convert.ToByte(words[i + 13]);
                }
                return Encoding.ASCII.GetString(bytes); ;
            }
            catch (IndexOutOfRangeException ex)
            {
                mainWindow.addConsoleText("SNMP exception: " + "No VRF in this router", Color.Red);
                return "No VRF";
            }
        }

        //! Obsługa operacji GET BULK dla zapytań wysyłanych przez użytkownika
        public List<Variable> GetBulk(List<string> oidList, string destIp, int port, string community, int maxRepetitions,
        int nonRepeaters, int timeout)
        {
            List<Variable> result = new List<Variable>();
            IPAddress ip;

            bool parsed = IPAddress.TryParse(destIp, out ip);
            if (!parsed)
            {
                if (destIp == null)
                {
                    Console.WriteLine("invalid host or wrong IP address found: ");
                    mainWindow.addConsoleText("invalid host or wrong IP address found", Color.Red);
                    return result;
                }

                foreach (IPAddress address in Dns.GetHostAddresses(destIp))
                {
                    if (address.AddressFamily == AddressFamily.InterNetwork)
                    {
                        ip = address;
                        break;
                    }
                }

                if (ip == null)
                {
                    Console.WriteLine("invalid host or wrong IP address found: " + destIp);
                    mainWindow.addConsoleText("invalid host or wrong IP address found: " + destIp, Color.Red);
                    return null;
                }
            }

            try
            {
                List<Variable> vList = new List<Variable>();
                for (int i = 0; i < oidList.Count; i++)
                {
                    Variable test = new Variable(new ObjectIdentifier(oidList[i]));
                    vList.Add(test);
                }

                IPEndPoint receiver = new IPEndPoint(ip, port);

                GetBulkRequestMessage message = new GetBulkRequestMessage(0,
                                                                          VersionCode.V2,
                                                                          new OctetString(community),
                                                                          nonRepeaters,
                                                                          maxRepetitions,
                                                                          vList);
                ISnmpMessage response = message.GetResponse(timeout, receiver);
                if (response.Pdu.ErrorStatus.ToInt32() != 0) // != ErrorCode.NoError
                {
                    throw ErrorException.Create(
                        "error in response",
                        receiver.Address,
                        response);
                }

                foreach (Variable variable in response.Pdu.Variables)
                {
                    result.Add(variable);
                }

                return result;

            }

            catch (SnmpException ex)
            {
                mainWindow.addConsoleText("SNMP exception: " + ex, Color.Red);
                return result;
            }
            return null;

        }

        //! Obsługa operacji GET NEXT dla zapytań wysyłanych przez użytkownika
        public List<Variable> GetNext(List<string> oidList, string destIp, int port, string community, int timeout)
        {
            List<Variable> result = new List<Variable>();
            IPAddress ip;

            bool parsed = IPAddress.TryParse(destIp, out ip);
            if (!parsed)
            {
                if (destIp == null)
                {
                    Console.WriteLine("invalid host or wrong IP address found: ");
                    mainWindow.addConsoleText("invalid host or wrong IP address found", Color.Red);
                    return result;
                }

                foreach (IPAddress address in Dns.GetHostAddresses(destIp))
                {
                    if (address.AddressFamily == AddressFamily.InterNetwork)
                    {
                        ip = address;
                        break;
                    }
                }

                if (ip == null)
                {
                    Console.WriteLine("invalid host or wrong IP address found: " + destIp);
                    mainWindow.addConsoleText("invalid host or wrong IP address found: " + destIp, Color.Red);
                    return null;
                }
            }

            try
            {
                List<Variable> vList = new List<Variable>();
                for (int i = 0; i < oidList.Count; i++)
                {
                    Variable test = new Variable(new ObjectIdentifier(oidList[i]));
                    vList.Add(test);
                }

                IPEndPoint receiver = new IPEndPoint(ip, port);


                GetNextRequestMessage message = new GetNextRequestMessage(0,
                                                                          VersionCode.V1,
                                                                          new OctetString(community),
                                                                          vList);
                ISnmpMessage response = message.GetResponse(timeout, receiver);
                if (response.Pdu.ErrorStatus.ToInt32() != 0) // != ErrorCode.NoError
                {
                    throw ErrorException.Create("error in response", receiver.Address, response);
                }

                foreach (Variable variable in response.Pdu.Variables)
                {                   
                    result.Add(variable);
                }

                return result;

            }
            catch (SnmpException ex)
            {
                mainWindow.addConsoleText("SNMP exception: " + ex, Color.Red);
                return null;
            }

        }

        //! Obsługa operacji GET TABLE dla zapytań wysyłanych przez użytkownika
        public Variable[,] GetTable(string oidTab, string destIp, int port, string community, int maxRepetitions, int timeout)
        {
            try
            {
                IPAddress ip;
                bool parsed = IPAddress.TryParse(destIp, out ip);
                if (!parsed)
                {
                    foreach (IPAddress address in Dns.GetHostAddresses(destIp))
                    {
                        if (address.AddressFamily == AddressFamily.InterNetwork)
                        {
                            ip = address;
                            break;
                        }
                    }

                    if (ip == null)
                    {
                        Console.WriteLine("invalid host or wrong IP address found: " + destIp);
                        mainWindow.addConsoleText("invalid host or wrong IP address found: " + destIp, Color.Red);
                        return null;
                    }
                }

                Variable[,] table = Messenger.GetTable(VersionCode.V1, new IPEndPoint(ip, port), new OctetString(community),
                                                      new ObjectIdentifier(oidTab), timeout, maxRepetitions, null);
                return table;

            }
            catch (SnmpException ex)
            {
                mainWindow.addConsoleText("SNMP exception: " + ex, Color.Red);
                return null;
            }
        }

        //! Obsługa operacji GET w trakcie weryfikacji laboratorium
        public List<Variable> snmpGet(List<string> oidList, TestCase tc, int port, string community)
        {
            TopologyWizzard.Router router = tc.ParentRouter;
            string destIp = router.IpAddress;
            int timeout = 1000;
            List<Variable> result = new List<Variable>();
            IPAddress ip;
            bool parsed = IPAddress.TryParse(destIp, out ip);

            if (!parsed)
            {
                if (destIp == null)
                {
                    Console.WriteLine("invalid host or wrong IP address found: ");
                    mainWindow.addConsoleText("invalid host or wrong IP address found", Color.Red);
                    return result;
                }
                try
                {
                    foreach (IPAddress address in Dns.GetHostAddresses(destIp))
                    {
                        if (address.AddressFamily == AddressFamily.InterNetwork)
                        {
                            ip = address;
                            break;
                        }
                    }
                }
                catch (SocketException ex)
                {
                    mainWindow.addConsoleText("SNMP exception: " + ex, Color.Red);
                }
                if (ip == null)
                {
                    Console.WriteLine("SNMP: invalid host or wrong IP address found: " + destIp);
                    mainWindow.addConsoleText("invalid host or wrong IP address found: " + destIp, Color.Red);
                    return null;
                }
            }

            try
            {
                List<Variable> vList = new List<Variable>();
                for (int i = 0; i < oidList.Count; i++)
                {
                    Variable test = new Variable(new ObjectIdentifier(oidList[i]));
                    vList.Add(test);
                }

                IPEndPoint receiver = new IPEndPoint(ip, port);

                foreach (
                    Variable variable in
                        Messenger.Get(VersionCode.V1, receiver, new OctetString(community), vList, timeout))
                {
                    //Console.WriteLine(variable);
                    result.Add(variable);
                }

                //System.Console.ReadLine();
                return result;


            }

            catch (SnmpException ex)
            {
                mainWindow.addConsoleText("SNMP exception: " + ex, Color.Red);
                if (ex is OperationException)
                {
                    if ((ex as OperationException).Details != "error in response. NoSuchName. Index: 1. Errored Object ID: .1.3.6.1.2.1.15.2.0")


                        tc.Log.Add(new KeyValuePair<string, string>("showError", (ex as OperationException).Details));


                }
                else
                {

                    tc.Log.Add(new KeyValuePair<string, string>("showError", ex.ToString()));


                }
                return null;
            }
        }

        //! Obsługa operacji GET TABLE w trakcie weryfikacji laboratorium
        public Variable[,] snmpGetTable(string oidTab, int port, string community, int maxRepetitions, TestCase tc)
        {
            try
            {
                TopologyWizzard.Router router = tc.ParentRouter;
                string destIp = router.IpAddress;
                int timeout = 5000;

                IPAddress ip;
                bool parsed = IPAddress.TryParse(destIp, out ip);
                if (!parsed)
                {
                    foreach (IPAddress address in Dns.GetHostAddresses(destIp))
                    {
                        if (address.AddressFamily == AddressFamily.InterNetwork)
                        {
                            ip = address;
                            break;
                        }
                    }

                    if (ip == null)
                    {
                        Console.WriteLine("invalid host or wrong IP address found: " + destIp);
                        mainWindow.addConsoleText("invalid host or wrong IP address found: " + destIp, Color.Red);
                        return null;
                    }
                }

                Variable[,] table = Messenger.GetTable(VersionCode.V1, new IPEndPoint(ip, port), new OctetString(community),
                                                      new ObjectIdentifier(oidTab), timeout, maxRepetitions, null);
                return table;

            }
            catch (SnmpException ex)
            {
                mainWindow.addConsoleText("SNMP exception: " + ex, Color.Red);
                return null;
            }
        }
    }
}
